﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BarSeries.cs" company="">
//   
// </copyright>
// <summary>
//   The bar series.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq; 

namespace Systemathics.FrameWork
{
    [Serializable] 
    public sealed class BarSeries : Series<Bar>
    {
        public BarType BarType      { get; set; }
        public int? BarSize         { get; set; }

        public event EventHandler<FireMessageEventArgs> EmitSeriesError;

        public BarSeries(string name) : base(name) { EmitBaseSeriesError += BarSeriesEmitSeriesError; }
        public BarSeries(string name, List<Bar> Bars) : base(name, Bars) { EmitBaseSeriesError += BarSeriesEmitSeriesError; }
        
        public Bar this[DateTime dateTime]
        {
            get
            {
                try
                {
                    lock (this)
                    {
                        return series.Find(delegate(Bar b) { return b.BeginTime == dateTime; });
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return new Bar();
                }
            }
        }
        public double this[DateTime dateTime, BarData BarData]
        {
            get
            {
                try
                {
                    lock (this)
                    {
                        return series.Find(delegate(Bar b) { return b.BeginTime == dateTime; })[BarData];
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return double.NaN;
                }
            }
        }
        public double this[int index, BarData BarData]
        {
            get
            {
                try
                {
                    lock (this)
                    {
                        if (index >= 0 && index <= series.Count) return series.ElementAt(index)[BarData];
                        else return double.NaN;
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return double.NaN;
                }
            }
        }

        private void BarSeriesEmitSeriesError(object sender, FireMessageEventArgs e) { if (EmitSeriesError != null) EmitSeriesError(sender, e); }

        public Cross Crosses(TimeSeries ts)
        {
            try
            {
                lock (this)
                {
                    if (ts.Count() > 1 && Count > 1)
                    {
                        if (Ago(1).Close < ts.Ago(1) && Last().Open > ts.Ago(1))
                            return Cross.Above;
                        if (Ago(1).Close > ts.Ago(1) && Last().Open < ts.Ago(1))
                            return Cross.Below;
                    }
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
            }
            return Cross.None;
        }
        public Cross Crosses(Bar b)
        {
            try
            {
                if (Last().Open < b.Median && Last().Close > b.Median)
                    return Cross.Above;
                if (Last().Open > b.Median && Last().Close < b.Median)
                    return Cross.Below;
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
            }

            return Cross.None;
        }
        public Decimal HighestHigh()
        {
            try
            {
                lock (this)
                {
                    return series.Max(b => b.High);
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                return 0m;
            }
        }
        public Decimal HighestHigh(int nElements)
        {
            int N = series.Count;
            if (nElements <= N)
            {
                lock (this)
                {
                    List<Bar> TempList = series.GetRange(N - nElements, nElements);
                    return TempList.Max(b => b.High);
                }
            }
            return 0m;
        }
        public Decimal HighestHigh(DateTime FromThisDate, DateTime ToThisDate)
        {
            if (ToThisDate > FromThisDate)
            {
                try
                {
                    lock (this)
                    {
                        var start = (int) GetIndex(FromThisDate);
                        var end = (int) GetIndex(ToThisDate);
                        List<Bar> TempList = series.GetRange(start, end - start);
                        return TempList.Max(b => b.High);
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return 0m;
                }
            }
            return 0m;
        }
        public Decimal HighestHigh(int start, int end)
        {
            if (series.Count >= end && end >= start)
            {
                try
                {
                    lock (this)
                    {
                        List<Bar> TempList = series.GetRange(start, end - start);
                        return TempList.Max(b => b.High);
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return 0m;
                }
            }
            return 0m;
        }
        public Decimal LowestLow()
        {
            try
            {
                lock (this)
                {
                    return series.Min(b => b.Low);
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                return 0m;
            }
        }
        public Decimal LowestLow(int nElements)
        {
            int N = series.Count;
            if (N > nElements)
            {
                lock (this)
                {
                    List<Bar> TempList = series.GetRange(N - nElements, nElements);
                    return TempList.Min(b => b.Low);
                }
            }
            return 0m;
        }
        public Decimal LowestLow(DateTime FromThisDate, DateTime ToThisDate)
        {
            if (ToThisDate > FromThisDate)
            {
                try
                {
                    lock (this)
                    {
                        var start = (int) GetIndex(FromThisDate);
                        var end = (int) GetIndex(ToThisDate);
                        List<Bar> TempList = series.GetRange(start, end - start);
                        return TempList.Min(b => b.Low);
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return 0m;
                }
            }
            return 0m;
        }
        public Decimal LowestLow(int start, int end)
        {
            if (series.Count >= end && end >= start)
            {
                try
                {
                    lock (this)
                    {
                        List<Bar> TempList = series.GetRange(start, end - start);
                        return TempList.Min(b => b.Low);
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return 0m;
                }
            }
            return 0m;
        }
        public void Remove(DateTime DateTime)
        {
            lock (this)
            {
                if (Contains(DateTime)) series.RemoveAt((int) GetIndex(DateTime));
            }
        }
        public bool Contains(DateTime dateTime)
        {
            try
            {
                if (series.Exists(delegate(Bar b) { return b.BeginTime == dateTime; })) return true;
                return false;
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                return false;
            }
        }
        public DateTime? GetDateTime(int index)
        {
            DateTime? result = null;
            try
            {
                lock (this)
                {
                    Bar b = series.ElementAt(index);
                    result = b.BeginTime;
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
            }

            return result;
        }
        public int? GetIndex(DateTime dateTime)
        {
            int? result = null;
            try
            {
                lock (this)
                {
                    if (Contains(dateTime)) result = series.IndexOf(series.Find(delegate(Bar b) { return b.BeginTime == dateTime; }));
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
            }

            return result;
        }
        public int Volume(int start, int end)
        {
            int size = 0;
            if (series.Count >= end && end >= start)
            {
                try
                {
                    lock (this)
                    {
                        List<Bar> TempList = series.GetRange(start, end - start);
                        foreach (Bar t in TempList) size += (int) t.Volume;
                        return size;
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return size;
                }
            }
            else return size;
        }
        public BarSeries ExpandBarSeriesBy(int Multiplier)
        {
            var Bars = new BarSeries(Ticker);
            if (Multiplier > BarSize && Count > 2)
            {
                lock (this)
                {
                    DateTime MarkUpDate = this[0].BeginTime;
                    int MarkUpIndex = 0, Index = 1;

                    var span = new TimeSpan(0, 0, (int) (this[0].Size*Multiplier));
                    if (span > (Last().EndTime - First().BeginTime)) return Bars;
                    try
                    {
                        foreach (Bar t in this)
                        {
                            int int2;
                            Index++;
                            Math.DivRem(Index, Multiplier, out int2);
                            if (int2 == 0)
                            {
                                try
                                {
                                    var BeginTime = this[MarkUpIndex].BeginTime;
                                    var EndTime = this[Index - 1].EndTime;
                                    var Open = this[MarkUpIndex].Open;
                                    var High = HighestHigh(MarkUpIndex, Index - 1);
                                    var Low = LowestLow(MarkUpIndex, Index - 1);
                                    var Close = this[Index - 1].Close;
                                    int volume = Volume(MarkUpIndex, Index - 1);

                                    var b = new Bar(BeginTime, EndTime, Open, High, Low, Close, (int) (this[0].Size*Multiplier), volume, 0);
                                    Bars.Add(b);

                                    MarkUpDate = EndTime;
                                    MarkUpIndex = Index;
                                }
                                catch (ArgumentOutOfRangeException)
                                {
                                    return Bars;
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    }
                }
            }

            return Bars;
        }
    }
}